💡 AI 인사이트

🤖 AI가 여기에 결과를 출력합니다...

댓글 커뮤니티

쿠팡이벤트

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

검색

    로딩 중이에요... 🐣

    [코담] 웹개발·실전 프로젝트·AI까지, 파이썬·장고의 모든것을 담아낸 강의와 개발 노트

    6. Aggregation(집계), Annotation(주석) | ✅ 저자: 이유정(박사)

    Aggregation (집계) 여러 행을 계산하여 하나의 결과로 요약하는 것 (예: 평균, 합계, 최대값, 개수 등) aggregate() 메서드를 사용하고 결과는 딕셔너리로 반환

    from django.db.models import Avg
    Book.objects.aggregate(avg_price=Avg('price'))
    # 결과: {'avg_price': 25000.0}
    

    Annotation (주석/주입된 계산 필드) 각 객체에 계산된 값을 함께 붙여주는 것 (예: 댓글 수, 주문 총액 등) annotate() 메서드를 사용하고 각 객체에 필드처럼 계산 결과가 추가됨

    from django.db.models import Count
    Author.objects.annotate(book_count=Count('books'))
    # 각 Author 객체에 book_count 속성이 붙음
    
    1. models.py 에서 데이터 스키마(필드들)를 정의해 두면,
    2. 그 위에 Aggregation(집계)이나 Annotation(주석) 메서드를 호출해서
    3. 모델이 가진 필드를 마치 사칙연산하듯이 더하고(Sum), 세고(Count), 평균내고(Avg), 최소·최대값을 구하고(Min/Max), 표준편차·분산까지 계산하거나,
    4. annotate() 로 쿼리셋의 각 객체별로 계산 결과를 속성으로 붙이는

    이런 모든 작업을 데이터베이스 레벨에서 한 줄의 ORM 호출로 처리할 수 있습니다. 혹은 더 나아가 Django의 F() 표현식을 섞어서

    from django.db.models import F, ExpressionWrapper, FloatField
    
    # 예: price * quantity 계산해서 annotate
    Order.objects.annotate(
        total_price=ExpressionWrapper(
            F('price') * F('quantity'),
            output_field=FloatField()
        )
    )
    

    처럼 필드 간 사칙연산도 가능합니다. 이처럼

    • 스키마 정의는 models.py
    • 그 위에 aggregate/annotate/F() 등을 써서
    • 실질적인 “계산 로직”을 한 줄로 작성 하면, 복잡한 SQL 없이도 ORM만으로 다양한 통계·계산을 손쉽게 처리할 수 있습니다.

    models.py – 아티스트, 앨범, 노래 모델 정의 첫 번째 이미지(빨간 화살표)에서 본 대로, ArtistAlbumSong 이 1:N 관계로 연결되어 있습니다.

    • Artist ↔ Album: 1 : N (related_name='albums')
    • Album ↔ Song: 1 : N (related_name='songs')

    aggregate() – 전체 통계 한 번에 계산 앨범 전체에 대한 요약 통계를 QuerySet.aggregate() 로 한 번에 뽑아냅니다. views.py

    from django.db.models import Count, Min, Max, Sum, Avg
    
    album_data = Album.objects.aggregate(
        album_count = Count('id'),          # 앨범 총 개수
        earliest_release = Min('release_date'),  # 가장 이른 발매일
        latest_release = Max('release_date'),  # 가장 늦은 발매일
        total_songs = Sum('songs'),         # 모든 앨범의 곡 개수 합
        average_duration = Avg('songs__duration')# 모든곡의 평균 재생시간
    )
    
    • 결과는 하나의 dict
    • Count, Min, Max, Sum, Avg 를 이용해 테이블 전체를 요약

    분산 통계 추가 (StdDev & Variance) views.py

    from django.db.models import StdDev, Variance
    
    album_data = Album.objects.aggregate(
      album_count        = Count('id'),
      earliest_release   = Min('release_date'),
      latest_release     = Max('release_date'),
      total_songs        = Sum('songs__id'),       # 총 곡 수
      average_duration   = Avg('songs__duration'), # 평균 곡 길이
      duration_stddev    = StdDev('songs__duration'),# 곡 길이의 표준편차
      duration_variance  = Variance('songs__duration')# 곡 길이의 분산
    )
    
    • StdDev: 표준편차
    • Variance: 분산

    default= 인자 사용 예 models.py 에 이렇게 정의되어 있습니다.

    class Song(models.Model):
        # … 기존 필드들
        score = models.IntegerField(default=0)
        # …
    

    views.py

    song_data = Song.objects.aggregate(
        total_score = Sum('score', default=0)
    )
    
    • score 필드가 아예 없는 경우에도 결과가 None 대신 0 반환

    filter= 파라미터로 조건부 집계 filter=Q(...) 에 지정된 조건(2020년 이후 발매)만 골라서 집계

    annotate() — 레코드별 계산 결과 붙이기

    from django.db.models import Count, Avg, Max, Sum
    
    albums = Album.objects.annotate(
        song_count = Count('songs'),  # 해당 앨범의 곡 개수
        average_song_duration = Avg('songs__duration'), 
        # 해당 앨범 곡 평균 시간
        
        longest_song_duration = Max('songs__duration'),# 최장 곡 시간
        total_duration        = Sum('songs__duration') # 총재생시간 합계
    )
    
    • aggregate() 가 “테이블 전체”에 대한 단일 값 반환이라면,
    • annotate() 는 “각 앨범 객체”에 계산된 필드를 추가해서 반환

    annotate()를 쓰면 “각 Album 객체”에 대해 계산된 값을 새로운 필드로 붙여준다는 예제입니다.

    from django.db.models import Count, Avg, Max, Sum
    
    # 각 앨범에 대해:
    #  - song_count : songs(related_name) 로 연결된 Song 의 개수
    #  - average_song_duration : songs__duration 필드의 평균
    #  - longest_song_duration : songs__duration 필드의 최댓값
    #  - total_duration : songs__duration 필드의 합계
    albums = Album.objects.annotate(
        song_count  = Count('songs'),
        average_song_duration = Avg('songs__duration'),
        longest_song_duration = Max('songs__duration'),
        total_duration = Sum('songs__duration'),
    )
    
    • annotate()는 내부적으로 GROUP BY album.id를 수행해서,
    • 각 앨범 행(row)에 대해 위에서 지정한 집계 함수를 실행하고,
    • 그 결과를 album.song_count, album.average_song_duration 등의 속성으로 붙여서 반환합니다.

    이렇게 하면:

    for alb in albums:
        print(
          alb.title, # 앨범 제목
          alb.song_count, # 곡 개수
          alb.average_song_duration, # 평균 곡 길이
          alb.longest_song_duration, # 최장 곡 길이
          alb.total_duration, # 총 재생 시간
    )
    

    반복문 한 번으로 “앨범별 통계”를 바로 꺼내 쓸 수 있습니다.

    TOP
    preload preload